$(document).ready( addGraftingOption );
$(document).mouseup( unsetPaperGrab );
$(document).mousedown( returnFalse );
$(document).mousemove( movePaperWad );

function returnFalse() { return false; }

function addGraftingOption() {

    optionsMenuItems.push( { text: 'Graft Trees View',
                             func: switchToGraftingView,
                             params: { } } );
}

function switchToGraftingView() {

    var localGlobal = globals;

    var mainPanel = $('#mainPanel');

    $('#leftPanel').hide('slow', alignTrashCan );

    mainPanel.unbind( 'mousemove' );
    
    if( localGlobal.cladeButton ) { localGlobal.cladeButton.hide(); }

    mainPanel.after(
        makeEl('div').attr( { 'id': 'rightPanel', 'class': 'scrollable fLeft width25 height98 blackBorder' } ).append(
            makeEl('div').attr( { 'id': 'trashCan', 'class': 'absoluteFront' } ).append( makeEl('img').attr( { 'height': '25', 'width': '25', 'src': '/' + globals.applicationName + '/static/images/trash.gif' } ) ), 
            makeEl('div').attr( { 'id': 'rightPanelTab' } ).append(
                makeEl('ul').attr( { 'id': 'tabSelector' } ).append(
                    makeEl('li').append( makeEl('a').attr( { 'href': '#rightPanelSourceContainer' } ).append( makeEl('span').attr( { 'class': 'smallerText' } ).text('Source Trees') ) ),
                    makeEl('li').append( makeEl('a').attr( { 'href': '#rightPanelGraftedContainer' } ).append( makeEl('span').attr( { 'class': 'smallerText' } ).text('Grafted Trees') ) ),
                    makeEl('li').append( makeEl('a').attr( { 'href': '#rightPanelSavedContainer' } ).append( makeEl('span').attr( { 'class': 'smallerText' } ).text('Saved Clades') ) ) ),

                makeEl('div').attr( { 'id': 'rightPanelSourceContainer' } ),
                makeEl('div').attr( { 'id': 'rightPanelGraftedContainer' } ),
                makeEl('div').attr( { 'id': 'rightPanelSavedContainer' } ) ) ) );

    ajaxRetrieveAllTreeInfo();
}

function alignTrashCan() {

    var rightPanel = $('#rightPanel');
    var rightPanelOffset = rightPanel.offset();

    $('#trashCan').css( { top: rightPanelOffset.top + 10,
                          left: rightPanelOffset.left + rightPanel.width() - 50 } );

}


function handleAllTreeInfoResponse( response ) {

    var responseObj = eval( "(" + response + ")" );

    var containers = { 'source': $('#rightPanelSourceContainer'),
                       'grafted': $('#rightPanelGraftedContainer'),
                       'saved': $('#rightPanelSavedContainer') };

    for( var type in responseObj ) {

        for( var i = 0, ii = responseObj[type].length; i < ii; i++ ) {

            containers[type].append(
                createRightPanelTreeItem( { id: responseObj[type][i].id,
                                            name: responseObj[type][i].name,
                                            creationDate: responseObj[type][i].creationDate,
                                            type: type } ) );
        }
    }
    
    $('#rightPanelTab').tabs( { select: handleTabSelect } );
}

function handleTabSelect( event, ui ) {

    if( ! $('#rightPanel').hasClass('scrollable') ) { $('#rightPanel').addClass('scrollable'); }
    $('.rightPanelTreeItem:hidden').show();
    $('#rightPanel .selected').removeClass('selected');
    
    $('#rightPanel div.visualContainer:visible').hide('slow').css( { top: 0, left: 0 } ).removeClass('absoluteFront').unbind('mouseup');
    
    globals.draggingInfo.currentlyDragging = false;
       
    hideVisualGraftingAnnotations();
}

function underlineThis() { $(this).addClass('underline'); }
function removeUnderline() { $(this).removeClass('underline'); }


function movePaperWad( p ) {

    var localGlobal = globals;

    if( ! localGlobal.paperGrabId ) { return; }

    var paperWad = $('#paperWad');

    if( ! paperWad.length ) { paperWad = createPaperWad(); }

    paperWad.css( { top: p.pageY - 10 , left: p.pageX - 10 } );
}

function createPaperWad() {

    paperWad = makeEl('div').attr( { 'id': 'paperWad', 'class': 'absoluteFront' } ).append( makeEl('img').attr( { 'class': 'transparentBackground', 'height': '20', 'width': '20', 'src': '/' + globals.applicationName + '/static/images/paperWad.gif' } ) );

    $('#mainPanel').append( paperWad );

    return paperWad;
}

            
function setPaperGrab( p ) {

    globals.paperGrabId = p.data.treeId;
}

function unsetPaperGrab( p ) {
    
    if( mouseOnEl( el: $('#trashCan'), x: p.pageX, y: p.pageY ) ) {
        trashTree();
    } else {
        globals.paperGrabId = undefined;
    }
}

function trashTree() {

    var localGlobal = globals;


}

function createRightPanelTreeItem( p ) {

    return makeEl('div').attr( { 'treeId': p.id, 'treeType': p.type, 'class': 'rightPanelTreeItem bottomBorder padding10p' } ).append(

        makeEl('div').attr( { 'class': 'infoHeader' } )
                     .bind( 'mouseup', { treeId: p.id, type: p.type }, handleRightPanelTreeItemClick )
                     .mouseover( addContextHighlight )
                     .mouseout( removeContextHighlight )
                     .bind( 'mousedown', { treeId: p.id }, setPaperGrab )
                     .append( makeEl('div').attr( { 'class': 'fLeft treeName' } ).text( p.name )
                                           .bind( 'click', { treeId: p.id, type: p.type }, handleRightPanelTreeNameClick )
                                           .mouseover( underlineThis )
                                           .mouseout( removeUnderline ),
                              makeEl('div').attr( { 'class': 'fRight' } ).text( p.creationDate ),
                              makeEl('div').attr( { 'class': 'clear' } ) ),

       makeEl('div').attr( { 'class': 'visualWrapper' } ).append(
            makeEl('div').attr( { 'class': 'visualContainer' } ).hide() ) ); 
}


function handleRightPanelTreeNameClick( p ) {

    globals.requestingTreeForMainPanel = true;

    ajaxGetTree( { treeId: p.data.treeId, type: p.data.type, success: renderTreeInMainPanel } );
}

function handleRightPanelTreeItemClick( p ) {
    
    if( ! $('#rightPanel').hasClass('scrollable') ) { $('#rightPanel').addClass('scrollable'); }
    $('.rightPanelTreeItem:hidden').show();

    globals.draggingInfo.currentlyDragging = false;

    $('#rightPanel .selected').removeClass('selected');
    var infoContainer = $( $('#rightPanel div[treeId=' + p.data.treeId + '] .infoHeader') ).addClass('selected');

    var nameContainer = $( $('#rightPanel div[treeId=' + p.data.treeId + '] div.treeName')[0] );

    if( mouseOnElement( { el: nameContainer, x: p.pageX, y: p.pageY } ) ) {  return; }

    var container = $( $('#rightPanel div[treeId=' + p.data.treeId + '] div.visualContainer')[0] )

    if( container.is(':visible') ) { container.hide('slow'); container.css( { top: 0, left: 0 } ).removeClass('absoluteFront').unbind('mouseup'); }

    else {
        
        $('#rightPanel div[treeType=' + p.data.type + '] div.visualContainer:visible').hide('slow').css( { top: 0, left: 0 } ).removeClass('absoluteFront').unbind('mouseup');
    
        if( container.children('svg').length ) { container.show('slow'); }

        else { ajaxGetTree( { treeId: p.data.treeId, type: p.data.type, success: renderTreeInRightPanel } ); }
    }
}

function renderTreeInRightPanel( response ) {
    
    var responseObj = eval( "(" + response + ")" );

    var tree = responseObj.tree;
    var treeId = responseObj.id;
    var treeType = responseObj.type;

    tree.isRoot = true;
    
    //traverse the tree for information needed before rendering 
    var treeInfo = getTreeInfo( { clade: tree } );

    var container = $('#rightPanel div[treeId=' + treeId + '] div.visualContainer')[0];

    var canvasHeight = $('#rightPanel').height() / 2;
    var canvasWidth = $('#rightPanel').width() * .90;

    var generationSeparation = ( canvasWidth - 20 ) / ( treeInfo.generations );
    var nodeSeparation = ( canvasHeight / ( treeInfo.tips + 1 ) );

    var canvas = Raphael( container, canvasWidth, canvasHeight );

    container = $(container);

    var pathString = new StringBuffer();
    
    preRender( { clade: tree,
                 currentTip: { x: ( generationSeparation * ( treeInfo.generations - 1 ) + 20 ), y: nodeSeparation },
                 scaleValue: { x: 1, y: 1 },
                 generationSeparation: generationSeparation,
                 nodeSeparation: nodeSeparation,
                 scaling: 'unscaled',
                 pathStrings: pathString,
                 renderInfoFun: rightPanelCladeRender,
                 canvas: canvas,
                 generation: treeInfo.generations } );


    createTreePath( { string: pathString.toString(),
                      canvas: canvas,
                      attr: { stroke: 'black',
                              "stroke-width": 2 } } );

    container.attr( { 'rootY': tree.coords.y,
                      'rootX': tree.coords['unscaled'].x } )
             .width( canvasWidth )
             .height( canvasHeight );


    container.draggable( { containment: 'document', 
                           drag: handleTreeDrag,
                           start: setTreeDrag,
                           stop: unsetTreeDrag } );

    container.show('slow');
}

function rightPanelCladeRender( p ) {

    var currentCoords = p.clade.coords;

    addNodeToPath( { pathStrings: p.pathStrings,
                     clade: p.clade,
                     scaling: 'unscaled' } );
}


function addNodeToPath( p ) {

    var coords = { x: p.clade.coords[p.scaling].x,
                  dx: p.clade.coords[p.scaling].dx,
                   y: p.clade.coords.y };

    if( p.clade.isRoot ) {

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( coords.y ).append( "l-20 0" );

    } else {

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( coords.y ).append( "l" ).append( coords.dx ).append( " 0" );
    }

    if( p.clade.children.length ) {

        var firstChildY = p.clade.children[0].coords.y;
        var lastChildY = p.clade.children[p.clade.children.length-1].coords.y;

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( firstChildY)
                     .append( "L" ).append( coords.x ).append( " " ).append( lastChildY );
    }
}

function handleTreeDrag( p ) {

    var localGlobal = globals;
    var localConfig = config;

    if( ! localGlobal.mainPanelTreeInfo.tree ) { return; }

    var movingTree = $(p.target);
    var movingTreeOffset = movingTree.offset();
    var movingTreeInfo = { top: movingTreeOffset.top,
                           height: movingTree.height(),
                           left: movingTreeOffset.left,
                           width: movingTree.width() };
    delete movingTreeOffset;

    var mainPanel = $('#mainPanel');
    var mainPanelOffset = mainPanel.offset();
    var mainPanelInfo = { top: mainPanelOffset.top,
                          height: mainPanel.height(),
                          left: mainPanelOffset.left,
                          width: mainPanel.width(),
                          scrollLeft: mainPanel.scrollLeft(),
                          scrollTop: mainPanel.scrollTop(),
                          element: mainPanel };
    delete movingPanelOffset;

    checkForMainPanelScroll( { localGlobal: localGlobal,
                               mainPanelInfo: mainPanelInfo,
                               movingTreeInfo: movingTreeInfo } );

    var closestCladeInfo = 
        findClosestClade(
            { x: movingTreeInfo.left, y: parseInt( movingTreeInfo.top ) + parseInt( movingTree.attr('rootY') ),
              closestClade: undefined, currentDistance: 1000000,
              scaleValue: { x: 1, y: 1 }, scaling: 'unscaled', clade: localGlobal.mainPanelTreeInfo.tree,
              offset: { y: mainPanelInfo.top - mainPanelInfo.scrollTop,
                        x: mainPanelInfo.left - mainPanelInfo.scrollLeft } } );


    if( closestCladeInfo.distance < 300 ) {

        var draggingInfo = localGlobal.draggingInfo;

        var closestClade = closestCladeInfo.clade;
        var mostRecentClade = draggingInfo.mostRecentClade;
        var closestCladeCoords = closestClade.coords;

        if( mostRecentClade && closestClade.id != mostRecentClade.id ) {

            var labelToMoveBack = localGlobal.labelInfo.labels[ mostRecentClade.id ];

            translateLabel( { clade: mostRecentClade,
                              label: labelToMoveBack,
                              fontSize: localConfig.fontSize.value,
                              buttonRadius: 0,
                              labelBuffer: localConfig.labelBuffer.value,
                              scaling: 'unscaled',
                              scaleValue: { x:1, y:1 } } );

            mostRecentClade = undefined;
        }
        
        if( closestClade.children.length == 0 ) {

            var pathString = getCladePath( { clade: closestClade } );

            if( draggingInfo.hidingPath ) {

                draggingInfo.hidingPath.attr( { path: pathString } ).show();

            } else {

                draggingInfo.hidingPath = createTreePath( { string: pathString,
                                                            canvas: localGlobal.mainPanelCanvas,
                                                            attr: { stroke: 'white',
                                                                    "stroke-width": localConfig.pathWidth.value } } );
            }

            pathString = new StringBuffer();

            pathString.append("M").append(closestCladeCoords.unscaled.x + closestCladeCoords.unscaled.dx).append(" ")
                      .append(closestCladeCoords.y).append("L").append( movingTreeInfo.left - mainPanelInfo.left + mainPanelInfo.scrollLeft ).append(" ")
                      .append( movingTreeInfo.top - mainPanelInfo.top + mainPanelInfo.scrollTop + parseInt( movingTree.attr('rootY')) );

            if( draggingInfo.graftingPath ) {
                
                draggingInfo.graftingPath.attr( { path: pathString.toString() } ).show();

            } else {

                draggingInfo.graftingPath = createTreePath( { string: pathString.toString(),
                                                              canvas: localGlobal.mainPanelCanvas,
                                                              attr: { stroke: 'blue',
                                                                      "stroke-width": localConfig.pathWidth.value } } );
            }

            if( closestClade.name ) {
  
                var label =  localGlobal.labelInfo.labels[closestClade.id];
                var bbox = label.getBBox();

                label.attr( { x: movingTreeInfo.left - mainPanelInfo.left + mainPanelInfo.scrollLeft - bbox.width,
                              y: movingTreeInfo.top - mainPanelInfo.top + mainPanelInfo.scrollTop + parseInt(movingTree.attr('rootY')) - bbox.height } ).toFront();
                
                draggingInfo.mostRecentClade = closestClade;
            }
           
            draggingInfo.graftingPath.graftType = 'replacement'; 
        } else {
            
            if( draggingInfo.hidingPath ) { draggingInfo.hidingPath.hide(); }

            pathString = new StringBuffer();

            var x = closestCladeCoords.unscaled.x;
            var boxY = movingTreeInfo.top - mainPanelInfo.top + mainPanelInfo.scrollTop + parseInt( movingTree.attr('rootY') );

            pathString.append("M").append(x).append(" ").append(closestCladeCoords.y).append("L").append(x).append(" ").append(boxY)
                      .append("M").append(x).append(" ").append(boxY).append("L").append( movingTreeInfo.left - mainPanelInfo.left + mainPanelInfo.scrollLeft)
                      .append(" ").append(boxY);


            if( draggingInfo.graftingPath ) {
                
                draggingInfo.graftingPath.attr( { path: pathString.toString() } ).show();

            } else {

                draggingInfo.graftingPath = createTreePath( { string: pathString.toString(),
                                                              canvas: localGlobal.skeletonCanvas,
                                                              attr: { stroke: 'blue',
                                                                      "stroke-width": localConfig.pathWidth.value } } );
            }
            
            draggingInfo.graftingPath.graftType = 'append'; 
        }

        draggingInfo.graftingPath.nodeId = closestClade.id;

   } else {

       hideVisualGraftingAnnotations();

   }
}

function setTreeDrag( p ) {

    var draggingInfo = globals.draggingInfo;

    if( draggingInfo.currentlyDragging == false ) {
        globals.draggingInfo.currentlyDragging = true;
        $(p.target).bind( 'mouseup', { }, handleGraftingTreeClick );
       
        var rightPanel = $('#rightPanel'); 

        rightPanel.removeClass('scrollable');

        var rightPanelBottom = rightPanel.offset().top + rightPanel.height();

        var rightPanelTrees = $('.rightPanelTreeItem');
        
        for( var i = 0, ii = rightPanelTrees.length; i < ii; i++ ) {
            var el = $(rightPanelTrees[i]);

            if( ( ( el.offset().top + el.height() ) + 20 > rightPanelBottom ) && ( ! el.find('.visualContainer:visible').length ) ) { el.hide(); }
        }
        
        var rightPanelTrees = $('.rightPanelTreeItem:visible');
        var visibleTreeContainer = $('#rightPanel div.visualContainer:visible').parentsUntil('.rightPanelTreeItem').parent('div');
        
        for( var i = 0, ii = rightPanelTrees.length; i < ii; i++ ) {
            var el = $(rightPanelTrees[i]);

            if( ( visibleTreeContainer.offset().top + visibleTreeContainer.height() ) + 20 > rightPanelBottom ) { el.hide(); }
            else { break; }
        }

        visibleTreeContainer.find('.visualContainer:visible').css( { top: 0, left: 0 } );
    }
}

function handleGraftingTreeClick( p ) {

    var localGlobal = globals;

    var contextMenu = isContextMenu();

    if( contextMenu && mouseOnElement( { el: contextMenu, x: p.pageX, y: p.pageY } ) ) { return; }

    else if( contextMenu ) { contextMenu.empty().remove(); }

    else if( p.which == 3 && localGlobal.draggingInfo.graftingPath ) {

        var graftingContainer = $('#rightPanel div.visualContainer:visible').parentsUntil('.rightPanelTreeItem').parent('div');

        var fun = ( localGlobal.mainPanelTreeInfo.type == 'grafted' ) ? ajaxGraft : showGraftDialogue;

        makeContextMenu( { container: $('#mainPanel'), 
                           menuItems: [ { text: 'Graft',
                                          func: fun,
                                          params: { graftingTreeId: graftingContainer.attr('treeId'),
                                                    graftingTreeType: graftingContainer.attr('treeType') } } ],
                                   x: p.pageX, y: p.pageY } );
    }
}

function showGraftDialogue( p ) {

    var params = p.data;

    Shadowbox.open( { content: "",
                      player: "html",
                      title: "Graft",
                      height: $(window).height() * .25,
                      width: $(window).width() * .45,
                      options: { onFinish: function() { renderGraftDialogue( params ); } } } );
}

function renderGraftDialogue( p ) {

    $('#sb-player').append(
        makeEl('div').attr( { 'class': 'topMargin1 fullWidth' } ).append(
            makeEl('div').attr( { 'class': 'right fLeft width15' } ).text('Name : '),
            makeEl('input').attr( { 'class': 'width30', 'id': 'graftedTreeName', 'type': 'text' } ),
            makeEl('div').attr( { 'class': 'clear' } ) ),
            
        makeEl('div').attr( { 'class': 'topMargin1 width85 right' } ).append(
            makeEl('a').attr( { 'class': 'rightMargin1', 'href': 'javascript:void(0);', id: 'shadowboxSubmitButton' } )
                       .bind( 'click', p , ajaxGraft )
                       .text('Add'),
            makeEl('a').attr( { 'href': 'javascript:void(0);' } ).click( closeShadowbox ).text('Close') ) );
}


function ajaxGraft( p ) {

    var localGlobal = globals;

    var name = ( localGlobal.mainPanelTreeInfo.type == 'graft' )
        ? ''
        : ( $('#graftedTreeName').val() )
            ? $('#graftedTreeName').val()
            : 'Untitled Grafted Tree';
            
    globals.requestingTreeForMainPanel = true;
     
    $.ajax( { url: makeAjaxURL( { argList: [ 'graft' ] } ),
              type: "POST",
              data: { nodeId: localGlobal.draggingInfo.graftingPath.nodeId,
                      baseTreeId: localGlobal.mainPanelTreeInfo.id,
                      baseTreeType: localGlobal.mainPanelTreeInfo.type,
                      graftingTreeId: p.data.graftingTreeId,
                      graftingTreeType: p.data.graftingTreeType,
                      graftType: localGlobal.draggingInfo.graftingPath.graftType,
                      newTreeName: name },
              success: handleGraftResponse } );

    closeShadowbox();
}

function handleGraftResponse( response ) {

    var responseObj = eval( "(" + response + ")" );

    var localGlobal = globals;

    var treeInfo = renderTreeInMainPanel( response );

    $('#rightPanel div.visualContainer:visible').hide('slow').css( { top: 0, left: 0 } ).removeClass('absoluteFront').unbind('mouseup');

    $('#rightPanel .selected').removeClass('selected');

    $('#rightPanel').addClass('scrollable');
    
    var yesFun = function() {
        $('#rightPanelGraftedContainer').append(
            createRightPanelTreeItem( { id: responseObj.id,
                                        name: responseObj.name,
                                        creationDate: responseObj.creationDate,
                                        type: 'grafted' } ) ) };

    ask( { 'text': 'Keep Graft ?',
           'yesFun': yesFun,
           'container': $('#mainPanel'),
           'noFun': ajaxRevertGraft } );
}

function ajaxRevertGraft() {

    $.ajax( { url: makeAjaxURL( { argList: [ 'revertGraft' ] } ),
              type: "POST",
              data: { treeId: globals.mainPanelTreeInfo.id } } );
}

function unsetTreeDrag() { }


function checkForMainPanelScroll( p ) {

    var mainTreeContainer = $('#raphaelContainer');

    p.mainPanelInfo.bottom = p.mainPanelInfo.top + p.mainPanelInfo.height;
    p.movingTreeInfo.bottom = p.movingTreeInfo.top + p.movingTreeInfo.height;

    //if there's a vertical scrollbar

    if( mainTreeContainer.height() > p.mainPanelInfo.height ) {

        if( ( p.mainPanelInfo.bottom - p.movingTreeInfo.bottom ) < 2 ) {

            if( ! p.localGlobal.scrollEvents.down ) {
                p.localGlobal.scrollEvents.down = true;

                setTimeout( verticalScroll, 0, { mainPanel: p.mainPanelInfo.element, scroll: 30 } );
            }
            
        } else {

            p.localGlobal.scrollEvents.down = false;
        }


        if( ( p.movingTreeInfo.top - p.mainPanelInfo.top ) < 2 ) {

            if( !p.localGlobal.scrollEvents.up ) {

                p.localGlobal.scrollEvents.up = true;

                setTimeout( verticalScroll, 0, { mainPanel: p.mainPanelInfo.element, scroll: -30 } );
            }

        } else {
            
            p.localGlobal.scrollEvents.up = false;
        }
    }
}

function verticalScroll( p ) {

    var localGlobal = globals;

    if( ( localGlobal.scrollEvents.down || localGlobal.scrollEvents.up ) && localGlobal.draggingInfo.currentlyDragging ) {

        p.mainPanel.scrollTop( p.mainPanel.scrollTop() + p.scroll );

        setTimeout( verticalScroll, 250, { mainPanel: p.mainPanel, scroll: p.scroll } );
    }
}

function hideVisualGraftingAnnotations() {

    var localGlobal = globals;
    var localConfig = config;
    var draggingInfo = localGlobal.draggingInfo;

    if( draggingInfo.hidingPath ) { draggingInfo.hidingPath.hide(); }
    if( draggingInfo.graftingPath ) { draggingInfo.graftingPath.hide(); }

    if( draggingInfo.mostRecentClade ) {

        var labelToMoveBack = localGlobal.labelInfo.labels[ draggingInfo.mostRecentClade.id ];

        translateLabel( { clade: draggingInfo.mostRecentClade,
                          label: labelToMoveBack,
                          fontSize: localConfig.fontSize.value,
                          buttonRadius: localConfig.buttonRadius.value,
                          labelBuffer: localConfig.labelBuffer.value,
                          scaling: 'unscaled',
                          scaleValue: { x:1, y:1 } } );

        draggingInfo.mostRecentClade = undefined;
    }
}
